package com.moss.cloud.common.core.utils;

import com.alibaba.fastjson.JSONObject;
import com.moss.cloud.common.core.annotation.ImportExcel;
import com.moss.cloud.common.core.exception.BaseException;
import com.moss.cloud.common.core.exception.SystemErrorType;
import com.moss.cloud.common.core.model.dto.ExcelDemoListDTO;
import com.moss.cloud.common.core.model.param.ImportParam;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 导入需求可以使用这个工具类 easypoi导入工具存在使用键盘del键删除一行数据后读取空行报错问题不建议使用
 * 此工具及其强大，避免了使用键盘del键删除一行数据后读取空行问题，增加了只能检验，字段上使用@ImportExcel 注解
 * 注解参数：fieldName 字段名称，nullType非空验证类型，regeRule格式验证正则表达式
 * 请看model.dto包中的ExcelDemoListDTO
 * 请注意阅读方法参数说明
 */
@Slf4j
public class ExcelUtil {
    public static final String REGEX = ",";
    public static final String REPLACEMENT = "";

    /**
     * excel导入
     * @param inputStream 文件流
     * @param pojoClass   需要转换成的实体类
     * @param params      导入文件参数
     * @param <T>         泛型
     * @return            List<T>
     */
    @SneakyThrows
    public static <T> List<T> importExcel(InputStream inputStream, Class<T> pojoClass, ImportParam params) {
        List<T> collection = new ArrayList<>();
        //这里需要注意XSSFWorkbook能够处理xlsx的文件
        //而HSSFWorkbook能够处理xls的文件，不然会报错，这样写是为了更好的兼容处理两种格式
        Workbook workbook = new XSSFWorkbook(inputStream);
        //这里是读取第几个sheet
        Sheet sheet = workbook.getSheetAt(0);
        //这里是读取总行数
        int startRow = params.getTitleRows() + params.getHeadRows();
        int rows = sheet.getPhysicalNumberOfRows();
        for (int i = startRow; i < rows; i++) {
            //获取每一行
            Row row = sheet.getRow(i);
            if (row != null) {
                int columns = row.getLastCellNum();
                StringBuffer stringBuffer = new StringBuffer();
                for (int j = 0; j < columns; j++) {
                    //获取每个单元格
                    Cell cell = row.getCell(j);
                    if (cell != null) {
                        //设置单元格类型
                        cell.setCellType(CellType.STRING);
                        //获取单元格数据
                        String cellValue = cell.getStringCellValue();
                        stringBuffer.append(cellValue + ",");
                    }
                }
                T pojo = readCell(stringBuffer, i, pojoClass);
                if (Objects.nonNull(pojo)) {
                    collection.add(pojo);
                }
            }
        }
        return collection;
    }

    /**
     * 读取每一列的数据
     * 此方法更多的是为了解决使用del键删除excel中的数据poi会读取到空数据的问题
     * 为了解决此问题，将每一行的数据都存储在StringBuffer中，如果StringBuffer的数据是空的，证明这一行数据都是空的，
     * 直接跳过此行数据，继续读取下一行
     * @param stringBuffer 每一行的数据
     * @param rowIndex     第几行
     * @param pojoClass    需要转换成的实体类
     * @param <T>          泛型
     * @return             T
     */
    @SneakyThrows
    public static <T> T readCell(StringBuffer stringBuffer, int rowIndex, Class<T> pojoClass) {
        T pojo = pojoClass.newInstance();
        Field[] fields = pojoClass.getDeclaredFields();
        String columns = stringBuffer.toString();
        log.info("去除符号校验数据:{}", StringUtils.isNotBlank(columns.replaceAll(REGEX, REPLACEMENT)));
        if (StringUtils.isNotBlank(columns.replaceAll(REGEX, REPLACEMENT))) {
            String newColumns = columns.substring(0, columns.length() - 1);
            log.info("去掉最后一个符号的数据:{}", newColumns);
            String[] arry = newColumns.split(",");
            if (fields.length != arry.length) {
                log.info("导入模板和实体不相符,实体类字段数量:{},模板列数量:{}", fields.length, arry.length);
                throw new BaseException(SystemErrorType.EXCEL_ERROR, "导入模板错误");
            }
            log.info("转化成数组的数据长度:{}", arry.length);
            for (int a = 0; a < arry.length; a++) {
                String cellValue = arry[a];
                Field item = pojoClass.getDeclaredField(fields[a].getName());
                checkCell(item,cellValue,rowIndex,a);
                log.info("字段类型:{}", item.getType());
                item.setAccessible(true);
                typeConversion(item, cellValue, pojo);
            }
            log.info("每一行转化成对象的数据:{}", JSONObject.toJSONString(pojo));
            return pojo;
        }
        return null;
    }

    /**
     * 数据类型转换
     * @param field  字段属性
     * @param value  列值
     * @param pojo   需要转换成的实体类
     * @param <T>    T
     * @throws IllegalAccessException
     */
    public static <T> void typeConversion(Field field, String value, T pojo) throws IllegalAccessException {
        if (field.getType().equals(String.class)) {
            field.set(pojo, value);
        }
        if (field.getType().equals(Long.class)) {
            field.set(pojo, Long.parseLong(value));
        }
        if (field.getType().equals(Integer.class)) {
            field.set(pojo, Integer.parseInt(value));
        }
        if (field.getType().equals(Double.class)) {
            field.set(pojo, Double.parseDouble(value));
        }
        if (field.getType().equals(Float.class)) {
            field.set(pojo, Float.parseFloat(value));
        }
        if (field.getType().equals(BigDecimal.class)) {
            field.set(pojo, new BigDecimal(value));
        }
    }

    /**
     * 只能数据校验
     * @param field      字段属性
     * @param cellValue  列值
     * @param rowIndex   第几行
     * @param cellIndex  第几列+1
     */
    public static void checkCell(Field field,String cellValue,int rowIndex,int cellIndex){
        ImportExcel excel = field.getAnnotation(ImportExcel.class);
        if (excel != null) {
            int l = cellIndex + 1;
            if (StringUtils.isNotBlank(excel.nullType())) {
                if (excel.nullType().equals("BLOCK")) {
                    if (StringUtils.isBlank(cellValue)) {
                        throw new BaseException(SystemErrorType.EXCEL_ERROR, "第" + rowIndex + "行第" + l + "列" + excel.fieldName() + "不能为空");
                    }
                }
                if (excel.nullType().equals("NULL")) {
                    if (cellValue == null) {
                        throw new BaseException(SystemErrorType.EXCEL_ERROR, "第" + rowIndex + "行第" + l + "列" + excel.fieldName() + "不能为空");
                    }
                }
            }
            if (StringUtils.isNotBlank(excel.regeRule())) {
                if(!cellValue.matches(excel.regeRule())){
                    throw new BaseException(SystemErrorType.EXCEL_ERROR, "第" + rowIndex + "行第" + l + "列" + excel.fieldName() + "格式不正确");
                }
            }
        }
    }

    /**
     * 测试方法
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("D:\\开发文档\\导入测试模板0.0.1.xlsx");
        InputStream inputStream = new FileInputStream(file);
        List<ExcelDemoListDTO> batchListAOList = importExcel(inputStream, ExcelDemoListDTO.class, new ImportParam());
        System.out.println(batchListAOList.toString());
    }
}
